#include "FunctionType.h"

#include "Function.h"

#include "TypeInstance.h"

CFunctionType::CFunctionType()
    :CType(
        STypeID(U"FunctionType",true )
        , ETypeFlags::FunctionPointer | ETypeFlags::Generic
        )
{
    GenericInfo= new CGenericInfo();
    //function type is special, it's placeholder num is dynamic.

    auto ReturnPlaceholder = new CPlaceholderType(
        STypeID(GetFullName()+U"::ReturnType",true )
        , ETypeFlags::None
        , 0
        , this
        );

    GenericInfo->PlaceholderTypes.Add(ReturnPlaceholder);

    auto ArgsPlaceholder = new CPlaceholderType(
        STypeID(GetFullName()+U"::Args...",true )
        , ETypeFlags::None
        , 1
        , this
        );

    ArgsPlaceholder->SetIsList(true);
    GenericInfo->PlaceholderTypes.Add(ArgsPlaceholder);
}

CFunctionType *CFunctionType::GetStaticType()
{
    static CFunctionType *StaticType = new CFunctionType() ;
    return StaticType;
}
static uint8_t RegisterFunctionType= ( CFunctionType::GetStaticType(), 0 );


class CFunctionTypeInstance : public ITypeInstance
{
protected:
    CFunctionTypeGeneralized* Type;

public:
    CFunctionTypeInstance(CFunctionTypeGeneralized* InType)
        :Type(InType)
    {
    }

    virtual uint32_t GetSize() override
    {
        return sizeof(CFunction*);
    }

    virtual void InitInstance(void* Memory) override
    {
        reinterpret_cast<CFunction**>(Memory)[0] = nullptr;
    }

    virtual void EraseInstance(void* Instance) override
    {
        reinterpret_cast<CFunction**>(Instance)[0] = nullptr;
    }

    virtual void CloneInstance(const void* Instance,void * TargetMemory) override
    {
        reinterpret_cast< CFunction**>(TargetMemory)[0] = reinterpret_cast< CFunction* const * >(Instance)[0];
    }

    virtual uint64_t GetInstanceHash(const void * Instance) const override
    {
        return (uint64_t)(reinterpret_cast< CFunction* const *>(Instance)[0]);
    }

    virtual bool IsInstanceEqual(const void * Instance0,const void * Instance1) const override
    {
        return reinterpret_cast<CFunction* const *>(Instance0)[0] == reinterpret_cast<CFunction* const *>(Instance1)[0];
    }

    virtual void* CreateInstance() override
    {
        return new CFunction*();
    }

    virtual void ResetAllocated(void * Memory) override
    {
        reinterpret_cast<CFunction**>(Memory)[0] = nullptr;
    }

    virtual CString ToDebugString(const void* InInstance) const override
    {
        return U"Function :"+ (reinterpret_cast<CFunction* const *>(InInstance)[0])->Name+U"()";
    }

};


// CString CFunctionTypeGeneralized::CreateTypeName(const SDecoratedType & ReturnType,const TVector<SDecoratedType>& ParamTypes)
// {
//     CString Name = CFunctionType::GetStaticType()->GetFullName()+U"<";
//     Name += ReturnType.BasicType->GetFullName();

//     Name +=U"(";

//     for (auto ParamType : ParamTypes)
//     {
//         Name += U",";
//         Name += ParamType.BasicType->GetFullName();
//     }
//     Name += U")>";
//     return Name;
// }

CFunctionTypeGeneralized::CFunctionTypeGeneralized(
    const SDecoratedType& InReturnType
    ,const TVector<SDecoratedType>&  InParamTypes
    )
    : CType(
        CFunctionType::CreateID(InReturnType,InParamTypes)
        //, CreateTypeName(InReturnType,InParamTypes)
        , ETypeFlags::FunctionPointer | ETypeFlags::Generalized
         |(CanFunctionUseInScript(InReturnType,InParamTypes)? ETypeFlags::None : ETypeFlags::CantUseInScript )
        )
{
    SetTypeArguments(InReturnType, InParamTypes);
    Instancable = new CFunctionTypeInstance(this);
}

void CFunctionTypeGeneralized::SetTypeArguments( const SDecoratedType & InReturnType,const TVector<SDecoratedType>& InParamTypes)
{    
    ReturnType = InReturnType;
    ParamTypes = InParamTypes;

    TVector<CType*> AllTypes;
    AllTypes.Add(ReturnType.BasicType);
    for(auto ParamType : ParamTypes)
    {
        AllTypes.Add(ParamType.BasicType);
    }
    reinterpret_cast<CGenericInfo*>(CFunctionType::GetStaticType()->GenericInfo)
        ->GenericMap.Add(AllTypes, this);

    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CFunctionType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(ReturnType.BasicType);
    for(auto ParamType : ParamTypes)
    {
        GeneralizedInfo->TypeArguments.Add(ParamType.BasicType);
    }

}


TMap<TVector<CType *>, CFunctionTypeGeneralized *>& CFunctionTypeGeneralized::GetFunctionTypeMap()
{
    static TMap<TVector<CType *>, CFunctionTypeGeneralized *> FunctionTypeMap;
    return FunctionTypeMap;
}



CFunctionTypeGeneralized *CFunctionType::FindFunctionType(const SDecoratedType & ReturnType, const TVector<SDecoratedType> &ParamTypes)
{
    TVector<CType*> AllTypes;
    AllTypes.Add(ReturnType.BasicType);
    for(auto ParamType : ParamTypes)
    {
        AllTypes.Add(ParamType.BasicType);
    }

    auto Found =reinterpret_cast<CGenericInfo*>(CFunctionType::GetStaticType()->GenericInfo)
        ->GenericMap.Find(AllTypes);

    if(Found)
    {
        return reinterpret_cast<CFunctionTypeGeneralized *>(*Found);
    }
    else
    {
        return nullptr;
    }
}

CString CFunctionType::CreateTypeName(const CString &ReturnType, const TVector<CString> &ParamTypes)
{
    TVector <CString> AllArugments;
    AllArugments.Add(ReturnType);
    for(auto ParamType : ParamTypes)
    {
        AllArugments.Add(ParamType);
    }

    TVector <CString> AllArugmentPlaceholders;
    AllArugmentPlaceholders.Add(U"ReturnType");
    for(int32_t i=0;i<ParamTypes.Num();i++)
    {
        AllArugmentPlaceholders.Add(U"Arg"+CString(i));
    }
    
    return STypeID::CreateTemplateTypeName(
        CFunctionType::GetStaticType()->GetFullName()
        ,AllArugments
        ,AllArugmentPlaceholders
        );
}

STypeID CFunctionType::CreateID(const SDecoratedType &ReturnType, const TVector<SDecoratedType> &ParamTypes)
{
    TVector<STypeID> AllParameterIDs;
    for(auto ParamType : ParamTypes)
    {
        AllParameterIDs.Add(ParamType.BasicType->ID);
    }

    return CreateID(ReturnType.BasicType->ID,AllParameterIDs);
}

STypeID CFunctionType::CreateID(const STypeID &ReturnType, const TVector<STypeID> &ParamTypes)
{
    TVector<STypeID> AllArguments;
    AllArguments.Add(ReturnType);
    AllArguments.Append(ParamTypes);
   
    TVector <CString> AllArugmentPlaceholders;
    AllArugmentPlaceholders.Add(U"ReturnType");
    for(int32_t i=0;i<ParamTypes.Num();i++)
    {
        AllArugmentPlaceholders.Add(U"Arg"+CString(i));
    }

    return STypeID(
        CFunctionType::GetStaticType()->ID
        ,AllArguments
        ,AllArugmentPlaceholders
    );
}

bool CFunctionTypeGeneralized::CanFunctionUseInScript(const SDecoratedType & ReturnType,const TVector<SDecoratedType>&  ParamTypes) 
{
    

    if(!ReturnType.BasicType->CanUseInScript())
    {
        return false;
    }
    

    for(auto ParamType : ParamTypes)
    {
        if(!ParamType.BasicType->CanUseInScript())
        {
            return false;
        }
    }

    return true;
}
